package com.atguigu.gmall.mq;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;


/**
 * @author lsj
 * @create 2021-08-29 11:38
 *
 * RabbitMQ的配置
 * 1、RabbitMQ的自动配置 RabbitAutoConfiguration
 * 1)、RabbitProperties 封装所有可以配置属性
 * 2)、RabbitTemplateConfigurer；自定义修改 RabbitTemplate
 * 3）、RabbitTemplate：  RedisTemplate，ElasticSearchTemplate; 操作rabbitmq的客户端
 * 4)、AmqpAdmin： 管理RabbitMQ，增删改查queue，exchange等
 *
 */
@EnableRabbit
@Configuration
public class AppRabbitConfig {


    //把要给rabbitmq中放的队列、交换机、绑定关系，@Bean放在容器中，也会自动创建

    /**
     * 订单的交换机
     * @return
     */
    @Bean
    public Exchange orderEventExchange(){
        /**
         * String name,  交换机名字
         * boolean durable,  持久化
         * boolean autoDelete, 是否自动删除
         * Map<String, Object> arguments, 交换机的参数设置
         */
        return new TopicExchange(RabbitConst.ORDER_EXCHANGE,true,false);
    }


    /**
     * 延时队列
     * @return
     */
    @Bean
    public Queue orderDelayQueue(){
        /**
         * String name,  队列的名字
         * boolean durable, 持久化
         * boolean exclusive, 排他的
         * boolean autoDelete, 自动删除
         * @Nullable Map<String, Object> arguments
         * x-dead-letter-exchange: order-event-exchange   // 信死了也不要丢，丢给哪个交换机
         * x-dead-letter-routing-key: order.release.order // 该死信交换机使用的路由键
         * x-message-ttl: 60000 // 消息过期时间是多久
         */
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange",RabbitConst.ORDER_EXCHANGE); // 设置死信交换机
        arguments.put("x-dead-letter-routing-key","order.release.order"); // 设置死信交换机的路由键
        arguments.put("x-message-ttl",60000*30); //30min过期
        return new Queue(RabbitConst.ORDER_DELAY_QUEUE,true,false,false,arguments);
    }

    @Bean
    public Queue orderReleaseOrderQueue(){
        return new Queue(RabbitConst.ORDER_RELEASE_QUEUE,true,false,false);
    }


    /**
     * 交换机和延时队列绑定
     * @return
     */
    @Bean
    public Binding orderCreateOrderBinding(){
        /**
         * String destination, 目的地
         * DestinationType destinationType, 目的地类型
         * String exchange, 交换机
         * String routingKey, 路由键
         * @Nullable Map<String, Object> arguments 参数设置项
         */
        return new Binding(RabbitConst.ORDER_DELAY_QUEUE,
                Binding.DestinationType.QUEUE,
                RabbitConst.ORDER_EXCHANGE,
                RabbitConst.CREATE_ORDER_RK,null);
    }

    /**
     * 交换机和死信队列的绑定
     * @return
     */
    @Bean
    public Binding releaseOrderBinding(){
        return new Binding(RabbitConst.ORDER_RELEASE_QUEUE,
                Binding.DestinationType.QUEUE,
                RabbitConst.ORDER_EXCHANGE,
                "order.release.order",null);
    }


    /**
     * 1、服务端确认模式 + 消费端手动确认模式配置
     */
    @Bean
    public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate();
        configurer.configure(template, connectionFactory);
        // CorrelationData correlationData, 消息的联合数据，消息的主键
        // boolean ack 服务返回true代表消息服务器已经收到,
        // @Nullable String cause
        template.setConfirmCallback((correlationData,ack,cause)->{
            if(ack){
                System.out.println("服务器收到了消息....");
            }else {
                System.out.println("服务器没有收到消息...."+correlationData.getReturnedMessage());
                //TODO 存数据库重试，再发送重试
            }
        });

        //
        //Message message, int replyCode, String replyText, String exchange, String routingKey
        template.setReturnCallback((message,replyCode,replyText,exchange,routingKey)->{
            //消息服务器没有处理成功（消息由交换机路由不到队列，即 交换机——>队列 过程失败了）
            System.out.println(message+"：投递到队列失败。");
            System.out.println("响应状态码："+replyCode);
            System.out.println("响应文本："+replyText);
            System.out.println("服务器收到的消息："+message);
            System.out.println("消息交换机："+exchange);
            System.out.println("消息路由件键："+routingKey);

        });
        return template;
    }

}
